#include <tenacitas.log/logger.h>
#include <tenacitas.log/level.h>
#include <tenacitas.log/thread_id.h>

#include <iostream>
#include <cstdint>
#include <string>
#include <memory>
#include <thread>
#include <chrono>

namespace tenacitas {
    namespace log {
        namespace test0 {

            struct simple_media {

                template <typename t_data>
                void write(const t_data & p_data) {
                    std::cout << "|" << p_data;
                }
                    
                void new_line(std::chrono::nanoseconds::rep p_nanosecs,
                              level p_level,
                              const char * p_file,
                              const char * p_function,
                              int64_t p_line) {
                    std::cout << '\n'
                              << m_th_id
                              << "|"
                              << p_nanosecs
                              << "|"
                              << p_level.str()
                              << "|"
                              << p_file
                              << "|"
                              << p_function
                              << "|"
                              << p_line;
                }

                simple_media(const thread_id & p_th_id)
                    : m_th_id(p_th_id){}

                simple_media(simple_media && p_media)
                    : m_th_id(p_media.m_th_id) {}


            private:
                thread_id m_th_id;

            };

            template <typename t_supplier>
            struct simple_media_factory {
                typedef simple_media media;
                media create(thread_id p_th_id) {
                    return media(p_th_id);
                }
            };

            template <typename t_supplier>
            struct simple_logger_factory {
                typedef typename tenacitas::log::logger<t_supplier> logger;

                logger & create(level p_level = level::info) {
                    m_logger.set_level(p_level);
                    return m_logger;
                }

            private:
                logger m_logger;
            };
            
            struct simple_supplier {

                typedef simple_media_factory<simple_supplier> media_factory;
                typedef simple_logger_factory<simple_supplier> logger_factory;

                media_factory & get_media_factory() {
                    return m_media_factory;
                }
                
                logger_factory & get_logger_factory() {
                    return m_logger_factory;
                }


            private:
                media_factory m_media_factory;
                logger_factory m_logger_factory;
            };

            template<typename t_test_class>
            bool run() {
                bool rc = true;
                
                std::cout << "test0::" << t_test_class::name << " ";
                
                if (t_test_class::run()) {
                    std::cout << " - PASSED" << std::endl;
                }
                else {
                    rc = false;
                    std::cout << " - FAILED" << std::endl;
                }
                return rc;
            }

            
            /** ------------------------------------------------------------- */
            class t0 {
            public:
                static const std::string name;

                typedef simple_supplier::logger_factory logger_factory;
                    
                typedef logger_factory::logger logger;

                static bool run() {

                    logger & l_logger = m_logger_factory.create();
                        
                    if (l_logger.can_log(level::info)) {
                        log_info(l_logger) << "ola" << 456.12;
                    }

                    return true;
                }

            private:
                static logger_factory m_logger_factory;
            };

            t0::logger_factory t0::m_logger_factory;

            const std::string t0::name = "t0";

            /** ------------------------------------------------------------- */
            class t1 {
            public:
                static const std::string name;

                typedef simple_supplier::logger_factory logger_factory;

                static void f() {

                    logger_factory::logger & l_logger =
                        m_logger_factory.create();

                    log_info(l_logger) << "YES!!!!!";

                    log_warn(l_logger) << "will crash!";
                }


                static bool run() {

                    logger_factory::logger & l_logger = m_logger_factory.create();

                    log_info(l_logger) << "bye" << 3587 << 'W';

                    std::thread l_th(t1::f);

                    std::this_thread::sleep_for(std::chrono::milliseconds(500));

                    l_th.join();

                    return true;
                }

            private:
                static logger_factory m_logger_factory;
            };

            t1::logger_factory t1::m_logger_factory;

            const std::string t1::name = "t1";

            /** ------------------------------------------------------------- */
            class t9999 {
            public:
                static const std::string name;

                typedef simple_supplier::logger_factory logger_factory;
                    
                typedef logger_factory::logger logger;

                static bool run() {

                    logger & l_logger = m_logger_factory.create();

                    log_fatal(l_logger, "CRASH!!");

                    return true;
                }

            private:
                static logger_factory m_logger_factory;
            };

            t9999::logger_factory t9999::m_logger_factory;

            const std::string t9999::name = "t9999";
            
            /** ------------------------------------------------------------- */
            class t2 {
            public:
                static const std::string name;

                typedef simple_supplier::logger_factory logger_factory;
                    
                typedef logger_factory::logger logger;

                static bool run() {

                    logger & l_logger = m_logger_factory.create(level::warn);

                    log_info(l_logger) << "what?";

                    return true;
                }

            private:
                static logger_factory m_logger_factory;
            };

            t2::logger_factory t2::m_logger_factory;

            const std::string t2::name = "t2";

            /** ------------------------------------------------------------- */
            static void run() {
                std::shared_ptr<simple_supplier> l_supplier(new
                                                            simple_supplier());

                typedef simple_supplier::logger_factory logger_factory;
                    
                typedef logger_factory::logger logger;

                logger::configure(l_supplier);
                
                run<t0>();
                run<t1>();
                run<t2>();
                run<t9999>();
            }
        }
    }
}
